package ga

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

//种群接口
type PopulationInterface interface {
	//替代某个个体的染色体
	ReplaceChromosom(offset int64, data ChromosomeInterface) bool
	//获取染色体
	GetChromosom(offset int64) (result ChromosomeInterface)
	//个体排序
	Sort()
	//获取最大适应度个体染色体
	GetMaxFitnessChromosome() (result ChromosomeInterface)
}

type ChromosomeSlice []ChromosomeInterface

type Population struct {
	numberOfChromosome          int64
	ChromosomeArray             ChromosomeSlice
	IsMaxFitnessChromosomeCache bool
	maxFitnessChromosomeCache   ChromosomeInterface
	maxFitnessChromosomeOffSet  int64
	FitnessFunc                 GetFitnessHandler
}

func BuildRandPopulation(num, length int64, min, max float64, f GetFitnessHandler) *Population {
	pop := &Population{
		numberOfChromosome: num,
		FitnessFunc:        f,
	}
	rand.Seed(time.Now().Unix())
	for i := int64(0); i < num; i++ {
		oneChrome := NewRandChromosome(length, min, max)
		oneChrome.SetFitnessFunc(f)
		pop.ChromosomeArray = append(pop.ChromosomeArray, oneChrome)
	}
	return pop
}

func (p *Population) SetChromosome(offset int64, data ChromosomeInterface) bool {
	if offset >= p.numberOfChromosome {
		return false
	}
	if p.ChromosomeArray[offset] == nil {
		p.ChromosomeArray[offset] = data
		p.IsMaxFitnessChromosomeCache = false
		return true
	}
	origin := p.ChromosomeArray[offset]

	if origin == data {
		return true
	}
	if p.IsMaxFitnessChromosomeCache {
		if p.maxFitnessChromosomeCache == data {
			return false
		}
		if p.maxFitnessChromosomeCache.GetFitness() > data.GetFitness() {
			p.IsMaxFitnessChromosomeCache = false
		} else {
			p.maxFitnessChromosomeCache = data
		}
	}
	p.ChromosomeArray[offset] = data
	return true
}

func (p *Population) ReplaceChromosom(offset int64, data ChromosomeInterface) bool {
	if offset >= p.numberOfChromosome {
		fmt.Println("out of rang, index=", offset, ", max=", p.numberOfChromosome)
	}
	return p.SetChromosome(offset, data)
}

func (p *Population) GetChromosom(offset int64) (result ChromosomeInterface) {
	if offset > p.numberOfChromosome {
		return nil
	}
	return p.ChromosomeArray[offset]
}

func (p *Population) GetSize() int64 {
	return p.numberOfChromosome
}

func (c ChromosomeSlice) Len() int {
	return len(c)
}

func (c ChromosomeSlice) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

func (c ChromosomeSlice) Less(i, j int) bool {
	return c[i].GetFitness() > c[j].GetFitness()
}

func (p *Population) Sort() {
	sort.Sort(p.ChromosomeArray)
	p.IsMaxFitnessChromosomeCache = true
	p.maxFitnessChromosomeCache = p.ChromosomeArray[0]
	p.maxFitnessChromosomeOffSet = 0
}

func (p *Population) GetMaxFitnessChromosome() (result ChromosomeInterface) {
	if p.IsMaxFitnessChromosomeCache {
		return p.maxFitnessChromosomeCache
	}
	p.maxFitnessChromosomeOffSet = 0
	var maxFitness float64 = 0
	var offset int64 = 0
	p.maxFitnessChromosomeCache = p.ChromosomeArray[0]
	for i := int64(0); i < p.numberOfChromosome; i++ {
		if p.ChromosomeArray[i].GetFitness() > maxFitness {
			offset = i
			maxFitness = p.maxFitnessChromosomeCache.GetFitness()
		}
	}
	p.IsMaxFitnessChromosomeCache = true
	p.maxFitnessChromosomeCache = p.ChromosomeArray[offset]
	p.maxFitnessChromosomeOffSet = offset
	return p.maxFitnessChromosomeCache
}
